﻿using System;
using System.Collections;
using System.Collections.Generic;
using System.Configuration;
using System.Reflection;
using System.Runtime.Remoting;
using System.Text;

namespace Library.Utilities
{
    public class ReflectionException : ApplicationException
    {
        public ReflectionException(string message, Exception inner)
            : base(message, inner)
        {
        }

        public ReflectionException(string message)
            : base(message)
        {
        }

        public ReflectionException()
        {
        }
    }

    public static class ReflectionUtility
    {
        #region Variable
        private static BindingFlags instanceFlag = BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic;
        private static BindingFlags staticFlag = BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.FlattenHierarchy;
        #endregion

        #region Information
        /// <summary>
        /// Get method parameter types
        /// </summary>
        /// <param name="method">The MethodInfo instance of the method to get parameters' types from</param>
        /// <returns>Array of parameters' types of the method</returns>
        public static Type[] GetParameterTypes(MethodBase method)
        {
            List<Type> types = new List<Type>();
            foreach (ParameterInfo parameter in method.GetParameters())
            {
                types.Add(parameter.ParameterType);
            }
            return types.ToArray();
        }

        /// <summary>
        /// Get method parameter names
        /// </summary>
        /// <param name="method">The MethodInfo instance of the method to get parameters' names from</param>
        /// <returns>Array of parameters' names of the method</returns>
        public static string[] GetParameterNames(MethodBase method)
        {
            List<string> names = new List<string>();
            foreach (ParameterInfo parameter in method.GetParameters())
            {
                names.Add(parameter.Name);
            }
            return names.ToArray();
        }

        /// <summary>
        /// Returns a specific FieldInfo object from the type
        /// </summary>
        /// <param name="type">The System.Type to search</param>
        /// <param name="fieldName">The case sensitive field name</param>
        /// <param name="searchHierarchy">A boolean indicating whether or not to search inherited fields</param>
        /// <returns>A FieldInfo object matching the constraints provided</returns>
        public static FieldInfo GetFieldInfo(Type type, string fieldName, bool searchHierarchy)
        {
            FieldInfo fieldInfo = type.GetField(fieldName, instanceFlag);
            if (fieldInfo == null) fieldInfo = type.GetField(fieldName, staticFlag);

            return (searchHierarchy && fieldInfo == null && type.BaseType != typeof(object)) ?
                                                                                                 GetFieldInfo(type.BaseType, fieldName, true) :
                                                                                                                                                  fieldInfo;
        }

        /// <summary>
        /// Returns a specific PropertyInfo object from the type
        /// </summary>
        /// <param name="type">The System.Type to search</param>
        /// <param name="propertyName">The case sensitive property name</param>
        /// <param name="searchHierarchy">A boolean indicating whether or not to search inherited properties</param>
        /// <returns>A PropertyInfo object matching the constraints provided</returns>
        public static PropertyInfo GetPropertyInfo(Type type, string propertyName, bool searchHierarchy)
        {
            PropertyInfo propertyInfo = type.GetProperty(propertyName, instanceFlag);
            if (propertyInfo == null) propertyInfo = type.GetProperty(propertyName, staticFlag);

            return (searchHierarchy && propertyInfo == null && type.BaseType != typeof(object)) ?
                                                                                                    GetPropertyInfo(type.BaseType, propertyName, true) :
                                                                                                                                                           propertyInfo;
        }

        /// <summary>
        /// Returns a list of MethodInfo objects from the type
        /// </summary>
        /// <param name="type">The System.Type to search</param>
        /// <param name="memberName">The case sensitive member name</param>
        /// <param name="types">A list of member types to search within</param>
        /// <param name="searchHierarchy">A boolean indicating whether or not to search inherited members</param>
        /// <returns>A list of MethodInfo objects matching the constraints provided</returns>
        private static List<MemberInfo> GetMemberInfo(Type type, string memberName, MemberTypes types, bool searchHierarchy)
        {
            List<MemberInfo> memberInfo = new List<MemberInfo>();
            memberInfo.AddRange((IEnumerable<MemberInfo>)type.GetMember(memberName, types, instanceFlag));
            memberInfo.AddRange((IEnumerable<MemberInfo>)type.GetMember(memberName, types, staticFlag));

            if (searchHierarchy && type.BaseType != typeof(object))
            {
                memberInfo.AddRange(GetMemberInfo(type.BaseType, memberName, types, true));
            }
            return memberInfo;
        }

        /// <summary>
        /// Returns a list of MethodInfo objects from the type
        /// </summary>
        /// <param name="type">The System.Type to search</param>
        /// <param name="memberName">The case sensitive member name</param>
        /// <param name="searchHierarchy">A boolean indicating whether or not to search inherited members</param>
        /// <returns>A list of MethodInfo objects matching the constraints provided</returns>
        private static List<MemberInfo> GetMemberInfo(Type type, string memberName, bool searchHierarchy)
        {
            List<MemberInfo> memberInfo = new List<MemberInfo>();
            memberInfo.AddRange((IEnumerable<MemberInfo>)type.GetMember(memberName, instanceFlag));
            memberInfo.AddRange((IEnumerable<MemberInfo>)type.GetMember(memberName, staticFlag));

            if (searchHierarchy && type.BaseType != typeof(object))
            {
                memberInfo.AddRange(GetMemberInfo(type.BaseType, memberName, true));
            }
            return memberInfo;
        }

        /// <summary>
        /// Returns a list of MemberInfo for members of the type of the specified MemberTypes
        /// </summary>
        /// <param name="type">The System.Type to search</param>
        /// <param name="memberType">A list of member types to search within</param>
        /// <param name="searchHierarchy">A boolean indicating whether or not to search inherited members</param>
        /// <returns>A list of MethodInfo objects matching the constraints provided</returns>
        private static List<MemberInfo> GetMemberInfo(Type type, MemberTypes memberType, bool searchHierarchy)
        {
            List<MemberInfo> memberInfo = new List<MemberInfo>();
            memberInfo.AddRange((IEnumerable<MemberInfo>)type.GetMembers(instanceFlag));
            memberInfo.AddRange((IEnumerable<MemberInfo>)type.GetMembers(staticFlag));

            List<MemberInfo> memberInfoChecked = new List<MemberInfo>();
            foreach (MemberInfo member in memberInfo)
            {
                if (member.MemberType == memberType) memberInfoChecked.Add(member);
            }

            if (searchHierarchy && type.BaseType != typeof(object))
            {
                memberInfoChecked.AddRange(GetMemberInfo(type.BaseType, memberType, true));
            }
            return memberInfoChecked;
        }

        /// <summary>
        /// Returns a list of all MemberInfo for the type
        /// </summary>
        /// <param name="type">The System.Type to search</param>
        /// <param name="searchHierarchy">A boolean indicating whether or not to search inherited members</param>
        /// <returns>A list of MethodInfo objects matching the constraints provided</returns>
        private static List<MemberInfo> GetMemberInfo(Type type, bool searchHierarchy)
        {
            List<MemberInfo> memberInfo = new List<MemberInfo>();
            memberInfo.AddRange((IEnumerable<MemberInfo>)type.GetMembers(instanceFlag));
            memberInfo.AddRange((IEnumerable<MemberInfo>)type.GetMembers(staticFlag));

            if (searchHierarchy && type.BaseType != typeof(object))
            {
                memberInfo.AddRange(GetMemberInfo(type.BaseType, true));
            }
            return memberInfo;
        }
        #endregion

        #region Action

        #region Class Actions
        /// <summary>
        /// Create a new instance of a class
        /// </summary>
        /// <param name="type">Type of the class to create a new instance from</param>
        /// <param name="arguments">Arguments to create a new instance</param>
        /// <returns>New instance created</returns>
        public static object CreateInstance(Type type, params object[] arguments)
        {
            return Activator.CreateInstance(type, arguments);
        }

        /// <summary>
        /// This is a deep-cloning function which uses reflection to clone an entire object tree
        /// </summary>
        /// <typeparam name="T">The type of object to clone</typeparam>
        /// <param name="target">The instance to create a clone of</param>
        /// <returns>An identical object tree</returns>
        public static T Clone<T>(T target)
        {
            T newInstance = (T)Activator.CreateInstance(target.GetType());
            FieldInfo[] fieldNames = newInstance.GetType().GetFields(instanceFlag);
            int index = 0;

            // Iterate through every field in the type and transfer the 
            // fields' values to the new instance.
            foreach (FieldInfo fieldInfo in fieldNames)
            {
                Type ICloneType = fieldInfo.FieldType.GetInterface("ICloneable", true);
                if (ICloneType != null)
                {
                    ICloneable IClone = (ICloneable)fieldInfo.GetValue(target);
                    if (IClone != null)
                    {
                        fieldNames[index].SetValue(newInstance, IClone.Clone());
                    }
                }
                else
                {
                    fieldNames[index].SetValue(newInstance, fieldInfo.GetValue(target));
                }

                Type IEnumerableType = fieldInfo.FieldType.GetInterface("IEnumerable", true);

                //if the type is enumerable...
                if (IEnumerableType != null)
                {
                    IEnumerable IEnum = (IEnumerable)fieldInfo.GetValue(target);
                    Type IListType = fieldNames[index].FieldType.GetInterface("IList", true);
                    Type IDicType = fieldNames[index].FieldType.GetInterface("IDictionary", true);
                    int index2 = 0;

                    // if this type is an enumerable list
                    if (IListType != null && IEnum != null)
                    {
                        IList list = (IList)fieldNames[index].GetValue(newInstance);

                        // step through each instance in the list
                        foreach (object instance in IEnum)
                        {
                            ICloneType = instance.GetType().GetInterface("ICloneable", true);

                            // if the type is clonable, clone and add it
                            if (ICloneType != null)
                            {
                                ICloneable clonable = (ICloneable)instance;
                                list[index2] = clonable.Clone();
                            }
                            index2++;
                        }
                    }
                    // otherwise, test to see if this is an enumerable dictionary type
                    else if (IDicType != null && IEnum != null)
                    {
                        IDictionary dictionary = (IDictionary)fieldNames[index].GetValue(newInstance);
                        index2 = 0;

                        // step through each entry in the dictionary
                        foreach (DictionaryEntry entry in IEnum)
                        {
                            ICloneType = entry.Value.GetType().GetInterface("ICloneable", true);

                            // if the entry instance is clonable, clone and add it
                            if (ICloneType != null)
                            {
                                ICloneable clonable = (ICloneable)entry.Value;
                                dictionary[entry.Key] = clonable.Clone();
                            }
                            index2++;
                        }
                    }
                }
                index++;
            }
            return newInstance;
        }

        /// <summary>
        /// This function will copy all values from one object instance to another
        /// instance of the same type
        /// </summary>
        /// <typeparam name="T">The type of both the toObject and fromObject</typeparam>
        /// <param name="fromObject">The instance to copy values TO</param>
        /// <param name="toObject">The instance to copy values FROM</param>
        public static void CopyObject<T>(T fromObject, T toObject)
        {
            FieldInfo[] fieldNames = toObject.GetType().GetFields(instanceFlag);
            int index = 0;

            // Iterate through every field in the type and transfer the 
            // fields' values to the new instance.
            foreach (FieldInfo fieldInfo in fieldNames)
            {
                Type ICloneType = fieldInfo.FieldType.GetInterface("ICloneable", true);
                if (ICloneType != null)
                {
                    ICloneable IClone = (ICloneable)fieldInfo.GetValue(fromObject);
                    if (IClone != null)
                    {
                        fieldNames[index].SetValue(toObject, IClone.Clone());
                    }
                }
                else
                {
                    fieldNames[index].SetValue(toObject, fieldInfo.GetValue(fromObject));
                }

                Type IEnumerableType = fieldInfo.FieldType.GetInterface("IEnumerable", true);

                //if the type is enumerable...
                if (IEnumerableType != null)
                {
                    IEnumerable IEnum = (IEnumerable)fieldInfo.GetValue(fromObject);
                    Type IListType = fieldNames[index].FieldType.GetInterface("IList", true);
                    Type IDicType = fieldNames[index].FieldType.GetInterface("IDictionary", true);
                    int index2 = 0;

                    // if this type is an enumerable list
                    if (IListType != null && IEnum != null)
                    {
                        IList list = (IList)fieldNames[index].GetValue(toObject);

                        // step through each instance in the list
                        foreach (object instance in IEnum)
                        {
                            ICloneType = instance.GetType().GetInterface("ICloneable", true);

                            // if the type is clonable, clone and add it
                            if (ICloneType != null)
                            {
                                ICloneable clonable = (ICloneable)instance;
                                list[index2] = clonable.Clone();
                            }
                            index2++;
                        }
                    }
                    // otherwise, test to see if this is an enumerable dictionary type
                    else if (IDicType != null && IEnum != null)
                    {
                        IDictionary dictionary = (IDictionary)fieldNames[index].GetValue(toObject);
                        index2 = 0;

                        // step through each entry in the dictionary
                        foreach (DictionaryEntry entry in IEnum)
                        {
                            ICloneType = entry.Value.GetType().GetInterface("ICloneable", true);

                            // if the entry instance is clonable, clone and add it
                            if (ICloneType != null)
                            {
                                ICloneable clonable = (ICloneable)entry.Value;
                                dictionary[entry.Key] = clonable.Clone();
                            }
                            index2++;
                        }
                    }
                }
                index++;
            }
        }

        /// <summary>
        /// Copies all fields from one object to another (The types of both objects must be related)
        /// </summary>
        /// <param name="fromObject">The instance to copy values from</param>
        /// <param name="toObject">The instance to copy values to</param>
        public static void MemberwiseCopy(object fromObject, object toObject)
        {
            Type fromType = fromObject.GetType();
            Type toType = toObject.GetType();

            Type smallerType;

            if (fromType.IsAssignableFrom(toType))
            {
                smallerType = fromType;
            }
            else if (toType.IsAssignableFrom(fromType))
            {
                smallerType = toType;
            }
            else
            {
                throw new ArgumentException("object types are not related");
            }

            FieldInfo[] fields = GetFields(smallerType);
            for (int i = 0; i < fields.Length; i++)
            {
                FieldInfo field = fields[i];
                field.SetValue(toObject, field.GetValue(fromObject));
            }
        }

        /// <summary>
        /// Copies all fields from one object to another
        /// </summary>
        /// <param name="srcObj">The instance to copy values from</param>
        /// <param name="desObj">The instance to copy values to</param>
        public static void CloneProperties(object srcObj, object desObj)
        {
            foreach (PropertyInfo srcPropertyInfo in srcObj.GetType().GetProperties())
            {
                string propertyName = srcPropertyInfo.Name;
                if (HasProperty(desObj, propertyName))
                {
                    PropertyInfo desPropertyInfo = desObj.GetType().GetProperty(srcPropertyInfo.Name);
                    object srcVal = srcPropertyInfo.GetValue(srcObj, null);
                    if (desPropertyInfo.GetSetMethod() != null)
                    {
                        desPropertyInfo.SetValue(desObj, srcVal, null);
                    }
                }
            }
        }

        private static bool HasProperty(object obj, string propertyName)
        {
            foreach (PropertyInfo pi in obj.GetType().GetProperties())
            {
                if (pi.Name.Equals(propertyName))
                {
                    return true;
                }
            }
            return false;
        }

        #endregion

        #region Method Actions
        /// <summary>
        /// Dynamically invokes a function on the target object.
        /// </summary>
        /// <param name="target">The target instance to invoke the method on</param>
        /// <param name="methodName">The name of the method to invoke</param>
        /// <returns>The result of the invoked method</returns>
        public static object CallMethod(object target, string methodName)
        {
            MethodInfo methodInfo = target.GetType().GetMethod(methodName, instanceFlag);

            if (methodInfo == null)
            {
                Throw(string.Concat("Reflector could not invoke the target method ", methodName,
                                    " because it does not exist."));
            }

            try
            {
                return methodInfo.Invoke(target, null);
            }
            catch (System.Reflection.TargetException ex)
            {
                Throw(string.Concat("The method, ", methodName, " in ", target.GetType().Name,
                                    " is not a valid target. See inner exception for more detail."), ex);
            }
            catch (System.Reflection.TargetInvocationException ex)
            {
                Throw(ex);
            }
            catch (System.Reflection.TargetParameterCountException ex)
            {
                Throw(ex);
            }
            return null;
        }

        /// <summary>
        /// Dynamically invokes a function on the target object.
        /// </summary>
        /// <param name="target">The target instance to invoke the method on</param>
        /// <param name="methodName">The name of the method to invoke</param>
        /// <param name="argument">An object to pass as an argument to the function invoked</param>
        /// <returns>The result of the invoked method</returns>
        public static object CallMethod(object target, string methodName, object argument)
        {
            MethodInfo methodInfo = target.GetType().GetMethod(methodName, instanceFlag);
            if (methodInfo == null)
            {
                Throw(string.Concat("Reflector could not invoke the target method ", methodName, " because it does not exist."));
            }

            try
            {
                return methodInfo.Invoke(target, new object[] { argument });
            }
            catch (System.Reflection.TargetException ex)
            {
                Throw(string.Concat("The method, ", methodName, " in ", target.GetType().Name,
                                    " is not a valid target. See inner exception for more detail."), ex);
            }
            catch (System.Reflection.TargetInvocationException ex)
            {
                Throw(ex);
            }
            catch (System.Reflection.TargetParameterCountException ex)
            {
                Throw(ex);
            }
            return null;
        }

        /// <summary>
        /// Dynamically invokes a function on the target object.
        /// </summary>
        /// <param name="target">The target instance to invoke the method on</param>
        /// <param name="methodName">The name of the method to invoke</param>
        /// <param name="arguments">An object array containing th arguments to pass. Note:
        /// arguments must be in the array in the order they are required in the function.</param>
        /// <returns>The result of the invoked method</returns>
        public static object CallMethod(object target, string methodName, params object[] arguments)
        {
            Type[] argTypes = GetTypeArray(arguments);
            MethodInfo methodInfo = target.GetType().GetMethod(methodName, instanceFlag, null, CallingConventions.HasThis, argTypes, null);

            if (methodInfo == null)
            {
                Throw(string.Concat("Reflector could not invoke the target method ", methodName,
                                    " because it does not exist."));
            }

            try
            {
                return methodInfo.Invoke(target, arguments);
            }
            catch (System.Reflection.TargetException ex)
            {
                Throw(string.Concat("The method, ", methodName, " in ", target.GetType().Name,
                                    " is not a valid target. See inner exception for more detail."), ex);
            }
            catch (System.Reflection.TargetInvocationException ex)
            {
                Throw(ex);
            }
            catch (System.Reflection.TargetParameterCountException ex)
            {
                Throw(ex);
            }
            return null;
        }

        /// <summary>
        /// Dynamically invokes a static function on the target type.
        /// </summary>
        /// <param name="type">The target type to invoke the method on</param>
        /// <param name="methodName">The name of the static method to invoke</param>
        /// <returns>The result of the invoked method</returns>
        public static object CallMethod(Type type, string methodName)
        {
            MethodInfo methodInfo = type.GetMethod(methodName, staticFlag);
            if (methodInfo == null)
            {
                Throw(string.Concat("Reflector could not invoke the target method ", methodName, " because it does not exist."));
            }

            try
            {
                return methodInfo.Invoke(new object(), new object[] { });
            }
            catch (System.Reflection.TargetException ex)
            {
                Throw(string.Concat("The method, ", methodName, " in ", type.Name,
                                    " is not a valid target. See inner exception for more detail."), ex);
            }
            catch (System.Reflection.TargetInvocationException ex)
            {
                Throw(ex);
            }
            catch (System.Reflection.TargetParameterCountException ex)
            {
                Throw(ex);
            }
            return null;
        }

        /// <summary>
        /// Dynamically invokes a static function on the target type.
        /// </summary>
        /// <param name="type">The target type to invoke the method on</param>
        /// <param name="methodName">The name of the static method to invoke</param>
        /// <param name="argument">An object to pass as an argument to the function invoked</param>
        /// <returns>The result of the invoked method</returns>
        public static object CallMethod(Type type, string methodName, object argument)
        {
            MethodInfo methodInfo = type.GetMethod(methodName, staticFlag);
            if (methodInfo == null)
            {
                Throw(string.Concat("Reflector could not invoke the target method ", methodName, " because it does not exist."));
            }

            try
            {
                return methodInfo.Invoke(new object(), new object[] { argument });
            }
            catch (System.Reflection.TargetException ex)
            {
                Throw(string.Concat("The method, ", methodName, " in ", type.Name,
                                    " is not a valid target. See inner exception for more detail."), ex);
            }
            catch (System.Reflection.TargetInvocationException ex)
            {
                Throw(ex);
            }
            catch (System.Reflection.TargetParameterCountException ex)
            {
                Throw(ex);
            }
            return null;
        }

        /// <summary>
        /// Dynamically invokes a static function on the target type.
        /// </summary>
        /// <param name="type">The target type to invoke the method on</param>
        /// <param name="methodName">The name of the static method to invoke</param>
        /// <param name="arguments">An object array containing th arguments to pass. Note:
        /// arguments must be in the array in the order they are required in the function.</param>
        /// <returns>The result of the invoked method</returns>
        public static object CallMethod(Type type, string methodName, params object[] arguments)
        {
            Type[] argTypes = GetTypeArray(arguments);
            MethodInfo methodInfo = type.GetMethod(methodName, staticFlag, null, CallingConventions.Standard, argTypes, null);

            if (methodInfo == null)
            {
                Throw(string.Concat("Reflector could not invoke the target method ", methodName, " because it does not exist."));
            }

            try
            {
                return methodInfo.Invoke(new object(), arguments);
            }
            catch (System.Reflection.TargetException ex)
            {
                Throw(string.Concat("The method, ", methodName, " in ", type.Name,
                                    " is not a valid target. See inner exception for more detail."), ex);
            }
            catch (System.Reflection.TargetInvocationException ex)
            {
                Throw(ex);
            }
            catch (System.Reflection.TargetParameterCountException ex)
            {
                Throw(ex);
            }
            return null;
        }
        #endregion

        #region Property/Field Actions
        /// <summary>
        /// Reads the value of either a property or field specified by the member name from the
        /// target class instance.
        /// </summary>
        /// <param name="target"></param>
        /// <param name="memberName">The case-sensitive member name</param>
        /// <returns>The member's value</returns>
        /// <exception cref="ArgumentException">ArgumentException is thrown when target is null or
        /// memberName is null or empty.</exception>
        /// <exception cref="ReflectorException">Exception is thrown if the field doesn't exist in the target type or
        /// if the member is write only.</exception>
        public static object Read(object target, string memberName, bool searchHierarchy)
        {
            Type type = target.GetType();
            List<MemberInfo> members = GetMemberInfo(type, memberName, MemberTypes.Field | MemberTypes.Property, searchHierarchy);

            if (members.Count == 0)
            {
                Throw(string.Concat("Type ", type.Name,
                                    " does not contain a definition for member ", memberName, "."));
            }

            switch (members[0].MemberType)
            {
                case MemberTypes.Field:
                    {
                        return ReadField(target, memberName, searchHierarchy, new object[] { });
                    }
                case MemberTypes.Property:
                    {
                        return ReadProperty(target, memberName, searchHierarchy, new object[] { });
                    }
                default:
                    {
                        Throw(string.Concat(members[0].MemberType.ToString(),
                                            " is not a valid MemberType to read from."));
                        break;
                    }
            }
            return null;
        }

        /// <summary>
        /// Reads the value of either a property or field specified by the member name from the
        /// target class instance.
        /// </summary>
        /// <param name="target"></param>
        /// <param name="memberName">The case-sensitive member name</param>
        /// <param name="arguments">These argumetns are only for use with members which require index values</param>
        /// <returns>The member's value</returns>
        /// <exception cref="ArgumentException">ArgumentException is thrown when target is null or
        /// memberName is null or empty.</exception>
        /// <exception cref="ReflectorException">Exception is thrown if the field doesn't exist in the target type or
        /// if the member is write only.</exception>
        public static object Read(object target, string memberName, bool searchHierarchy, params object[] arguments)
        {
            Type type = target.GetType();
            List<MemberInfo> members = GetMemberInfo(type, memberName, MemberTypes.Field | MemberTypes.Property, searchHierarchy);

            if (members.Count == 0)
            {
                Throw(string.Concat("Type ", type.Name, " does not contain a definition for member ", memberName, "."));
            }

            switch (members[0].MemberType)
            {
                case MemberTypes.Field:
                    {
                        return ReadField(target, memberName, searchHierarchy, arguments);
                    }
                case MemberTypes.Property:
                    {
                        return ReadProperty(target, memberName, searchHierarchy, arguments);
                    }
                default:
                    {
                        Throw(string.Concat(members[0].MemberType.ToString(), " is not a valid MemberType to read from."));
                        break;
                    }
            }
            return null;
        }

        /// <summary>
        /// Writes the value to the property or field specified by the member name using auto type conversion.
        /// </summary>
        /// <param name="target">The class instance containing the member to write to</param>
        /// <param name="memberName">The case-sensitive member name</param>
        /// <param name="value">The value to write</param>
        /// <returns>True if writing was succesful, false if otherwise</returns>
        /// <exception cref="ArgumentException">ArgumentException is thrown when target is null or
        /// memberName is null or empty.</exception>
        /// <exception cref="ReflectorException">Exception is thrown if the field doesn't exist in the target type,
        ///	if the member is write only, if the target member and value type are incompatible, or if the
        ///	additional arguments passed in are incorrect.</exception>
        public static void Write(object target, string memberName, bool searchHierarchy, object value)
        {
            Type type = target.GetType();
            List<MemberInfo> members = GetMemberInfo(type, memberName, MemberTypes.Field | MemberTypes.Property, searchHierarchy);

            if (members.Count == 0)
            {
                Throw(string.Concat("Type ", type.Name,
                                    " does not contain a definition for member ", memberName, "."));
            }

            switch (members[0].MemberType)
            {
                case MemberTypes.Field:
                    WriteField(target, memberName, value, searchHierarchy, new object[] { });
                    break;
                case MemberTypes.Property:
                    WriteProperty(target, memberName, value, searchHierarchy, new object[] { });
                    break;
                default:
                    Throw(string.Concat(members[0].MemberType.ToString(),
                                        " is not a valid MemberType to write to."));
                    break;
            }
        }

        /// <summary>
        /// Writes the value to the property or field specified by the member name using auto type conversion.
        /// </summary>
        /// <param name="target">The class instance containing the member to write to</param>
        /// <param name="memberName">The case-sensitive member name</param>
        /// <param name="value">The value to write</param>
        /// <param name="arguments">These argumetns are only for use with members which require index values</param>
        /// <returns>True if writing was succesful, false if otherwise</returns>
        /// <exception cref="ArgumentException">ArgumentException is thrown when target is null or
        /// memberName is null or empty.</exception>
        /// <exception cref="ReflectorException">Exception is thrown if the field doesn't exist in the target type,
        ///	if the member is write only, if the target member and value type are incompatible, or if the
        ///	additional arguments passed in are incorrect.</exception>
        public static void Write(object target, string memberName, object value, bool searchHierarchy, params object[] arguments)
        {
            Type type = target.GetType();
            List<MemberInfo> members = GetMemberInfo(type, memberName, MemberTypes.Field | MemberTypes.Property, searchHierarchy);

            if (members.Count == 0)
            {
                Throw(string.Concat("Type ", type.Name,
                                    " does not contain a definition for member ", memberName, "."));
            }

            switch (members[0].MemberType)
            {
                case MemberTypes.Field:
                    WriteField(target, memberName, value, searchHierarchy, arguments);
                    break;
                case MemberTypes.Property:
                    WriteProperty(target, memberName, value, searchHierarchy, arguments);
                    break;
                default:
                    Throw(string.Concat(members[0].MemberType.ToString(),
                                        " is not a valid MemberType to write to."));
                    break;
            }
        }

        /// <summary>
        /// Reads the value of the specified field from the target class instance.
        /// </summary>
        /// <param name="target">The instance to read the field value from</param>
        /// <param name="fieldName">The Case-sensitive field name</param>
        /// <param name="arguments">The ordered list of arguments to use for reading hashtables and collections</param>
        /// <returns>The field's value</returns>
        /// <exception cref="ReflectorException">Exception is thrown if the field is read only or an error occurs during read.</exception>
        private static object ReadField(object target, string fieldName, bool searchHierarchy, object[] arguments)
        {
            FieldInfo fieldInfo = GetFieldInfo(target.GetType(), fieldName, searchHierarchy);
            object value = (fieldInfo.GetValue(target));
            Type valueType;

            if (arguments.Length == 0)
            {
                return value;
            }
            else if (IsDictionary(fieldInfo.FieldType))
            {
                IDictionary dictionary = ((IDictionary)value);

                if (arguments.Length == 1)
                {
                    return dictionary[arguments[0]];
                }
                else
                {
                    valueType = dictionary.GetType().GetGenericArguments()[1];
                    IList values = (IList)Activator.CreateInstance(typeof(List<>).MakeGenericType(valueType));

                    foreach (object key in arguments)
                    {
                        values.Add(dictionary[key]);
                    }

                    return values;
                }
            }
            else
            {
                IList collection = ((IList)value);
                valueType = collection[0].GetType();

                if (arguments.Length == 1)
                {
                    return collection[(int)arguments[0]];
                }
                else
                {
                    valueType = collection.GetType().GetGenericArguments()[0];
                    IList values = (IList)Activator.CreateInstance(typeof(List<>).MakeGenericType(valueType));

                    foreach (object index in arguments)
                    {
                        values.Add(collection[(int)index]);
                    }

                    return values;
                }
            }
        }

        /// <summary>
        /// Reads the value of the specified property from the target class instance.
        /// </summary>
        /// <param name="target">The instance to read the proerty value from</param>
        /// <param name="propertyName">The Case-sensitive property name</param>
        /// <param name="arguments">The ordered list of arguments to use for reading values from dictionaries and collections</param>
        /// <returns>The property's value</returns>
        /// <exception cref="ReflectorException">Exception is thrown if the field doesn't exist or cannot be read.</exception>
        private static object ReadProperty(object target, string propertyName, bool searchHierarchy, object[] arguments)
        {
            PropertyInfo propertyInfo = GetPropertyInfo(target.GetType(), propertyName, searchHierarchy);
            if (!propertyInfo.CanRead)
            {
                Throw(string.Concat("The property ", propertyName, " from object type ",
                                    target.GetType().Name, " is read only and cannot be read."));
            }

            if (propertyInfo.GetIndexParameters().Length == 0)
            {
                if (arguments.Length == 0)
                {
                    return (propertyInfo.GetValue(target, arguments));
                }
                else if (IsDictionary(propertyInfo.PropertyType))
                {
                    IDictionary dictionary = ((IDictionary)propertyInfo.GetValue(target, null));

                    if (arguments.Length == 1)
                    {
                        return dictionary[arguments[0]];
                    }
                    else
                    {
                        Type valueType = dictionary.GetType().GetGenericArguments()[1];
                        IList values = (IList)Activator.CreateInstance(typeof(List<>).MakeGenericType(valueType));

                        foreach (object key in arguments)
                        {
                            values.Add(dictionary[key]);
                        }

                        return values;
                    }
                }
                else
                {
                    IList collection = ((IList)propertyInfo.GetValue(target, null));

                    if (arguments.Length == 1)
                    {
                        return collection[(int)arguments[0]];
                    }
                    else
                    {
                        Type valueType = collection.GetType().GetGenericArguments()[0];
                        IList values = (IList)Activator.CreateInstance(typeof(List<>).MakeGenericType(valueType));

                        foreach (object index in arguments)
                        {
                            values.Add(collection[(int)index]);
                        }

                        return values;
                    }
                }
            }
            else
            {
                return (propertyInfo.GetValue(target, arguments));
            }
        }

        /// <summary>
        /// Writes the value to the member specified by the member name using auto type conversion.
        /// </summary>
        /// <param name="target">The class instance containing the member to write to</param>
        /// <param name="fieldName">The Case-sensitive member name</param>
        /// <param name="value">The value to write</param>
        /// <param name="arguments">The ordered list of arguments to use for writing values to dictionaries and collectrions</param>
        /// <returns>True if writing was succesful, false if otherwise</returns>
        private static void WriteField(object target, string fieldName, object value, bool searchHierarchy, object[] arguments)
        {
            FieldInfo fieldInfo = GetFieldInfo(target.GetType(), fieldName, searchHierarchy);
            Type fieldType = fieldInfo.FieldType;
            Type valueType = (value == null) ? fieldType : value.GetType();

            try
            {
                // If the types are compatible, no conversion is required, simply write the value
                if (fieldType.Equals(valueType))
                {
                    fieldInfo.SetValue(target, value);
                }
                // If the types are both primitives, use Convert.Changetype
                else if (fieldType.IsValueType && valueType.IsValueType)
                {
                    fieldInfo.SetValue(target, Convert.ChangeType(value, fieldType));
                }
                // This block of code is a little clumsy and needs some refactoring, for now
                // it works. It handles writing whatever the user has passed in for the value and
                // arguments and determines how to use those to add values to the target collection
                else if (IsCollection(fieldType) && fieldType.IsGenericType && !IsDictionary(fieldType))
                {
                    // Gets a reference to the target collection as an IList
                    IList list = (IList)Read(target, fieldName, searchHierarchy);

                    // If arguments equal 0, then this is just a plain add
                    if (arguments.Length == 0)
                    {
                        // If the value the user passed in was a collection, cast the list as
                        // a generic list of objects to use the AddRange functionality.
                        if (IsCollection(value))
                        {
                            CallMethod(list, "AddRange", value);
                        }
                        // Otherwise just add the single value
                        else
                        {
                            list.Add(value);
                        }
                    }
                    // If arguments were passed, the the user intends to have the value(s)
                    // inserted at a specific index.
                    else
                    {
                        // If the value is a collection use InsertRange
                        if (IsCollection(value))
                        {
                            CallMethod(list, "InsertRange", value, (int)arguments[0]);
                        }
                        // Otherwise insert the value at the specified index
                        else
                        {
                            list.Insert((int)arguments[0], value);
                        }
                    }
                }
                // If the field is a dictionary this code handles how the value(s) and argument(s)
                // are inserted into the dictionary
                else if (IsDictionary(fieldType) && arguments.Length > 0)
                {
                    IDictionary dictionary = (IDictionary)Read(target, fieldName, searchHierarchy);
                    // if there is one argument, then the value is meant to be added with
                    // under that argument as a key
                    if (arguments.Length == 1)
                    {
                        dictionary.Add(arguments[0], value);
                    }
                    // if there are as many arguments as values in the collection then
                    // each argument is a key to each value in the order provided.
                    else if (IsCollection(value))
                    {
                        IList list = value as IList;
                        if (arguments.Length == (list).Count)
                        {
                            int index = 0;
                            foreach (object key in arguments)
                            {
                                dictionary.Add(key, (list)[index]);
                                index++;
                            }
                        }
                    }
                }
                // Guids require special treatment because convert will not handle conversion
                // between strings and Guids.
                else if (fieldType.Equals(typeof(Guid)))
                {
                    fieldInfo.SetValue(target, new Guid(value.ToString()));
                }
            }
            catch (System.FieldAccessException ex)
            {
                Throw(string.Concat("Error reading field, ", fieldName, ", from type ",
                                    target.GetType().Name), ex);
            }
            catch (System.ArgumentException ex)
            {
                Throw(string.Concat("Error reading field, ", fieldName, ", from type ",
                                    target.GetType().Name), ex);
            }
            catch (System.IndexOutOfRangeException ex)
            {
                Throw(string.Concat("Error reading field, ", fieldName, ", from type ",
                                    target.GetType().Name), ex);
            }
            catch (System.InvalidCastException ex)
            {
                Throw(string.Concat("Error reading field, ", fieldName, ", from type ",
                                    target.GetType().Name), ex);
            }
        }

        /// <summary>
        /// Writes the value to the property specified in the field name using auto type conversion.
        /// </summary>
        /// <param name="target">The class instance containing the property to write to</param>
        /// <param name="propertyName">The Case-sensitive property name</param>
        /// <param name="value">The value to write</param>
        /// <param name="arguments">The ordered list of arguments to use for writing values to dictionaries and collectrions</param>
        /// <returns>True if writing was succesful, false if otherwise</returns>
        private static void WriteProperty(object target, string propertyName, object value, bool searchHierarchy, object[] arguments)
        {
            PropertyInfo propertyInfo = GetPropertyInfo(target.GetType(), propertyName, searchHierarchy);
            Type propertyType = propertyInfo.PropertyType;
            Type valueType = (value == null) ? propertyType : value.GetType();

            if (propertyInfo.CanWrite == false && !IsCollection(propertyType))
            {
                Throw("Error writing value to read only property " + propertyName +
                      " on object type " + target.GetType().Name);
            }

            try
            {
                // If the types are compatible, no conversion is required, simply write the value
                if (propertyType.Equals(valueType))
                {
                    propertyInfo.SetValue(target, value, arguments);
                }
                // If the types are both primitives, use Convert.Changetype
                else if (propertyType.IsValueType && valueType.IsValueType)
                {
                    propertyInfo.SetValue(target, Convert.ChangeType(value, propertyType), arguments);
                }
                // This block of code is a little clumsy and needs some refactoring, for now
                // it works. It handles writing whatever the user has passed in for the value and
                // arguments and determines how to use those to add values to the target collection
                else if (IsCollection(propertyType) && propertyType.IsGenericType && !IsDictionary(propertyType))
                {
                    // Gets a reference to the target collection as an IList
                    IList list = (IList)Read(target, propertyName, searchHierarchy);

                    // If arguments equal 0, then this is just a plain add
                    if (arguments.Length == 0)
                    {
                        // If the value the user passed in was a collection, cast the list as
                        // a generic list of objects to use the AddRange functionality.
                        if (IsCollection(value))
                        {
                            CallMethod(list, "AddRange", value);
                        }
                        // Otherwise just add the single value
                        else
                        {
                            list.Add(value);
                        }
                    }
                    // If arguments were passed, the the user intends to have the value(s)
                    // inserted at a specific index.
                    else
                    {
                        // If the value is a collection use InsertRange
                        if (IsCollection(value))
                        {
                            CallMethod(list, "InsertRange", value, (int)arguments[0]);
                        }
                        // Otherwise insert the value at the specified index
                        else
                        {
                            list.Insert((int)arguments[0], value);
                        }
                    }
                }
                // If the property is a dictionary this code handles how the value(s) and argument(s)
                // are inserted into the dictionary
                else if (IsDictionary(propertyType) && arguments.Length > 0)
                {
                    IDictionary dictionary = (IDictionary)Read(target, propertyName, searchHierarchy);
                    // if there is one argument, then the value is meant to be added with
                    // under that argument as a key
                    if (arguments.Length == 1)
                    {
                        dictionary.Add(arguments[0], value);
                    }
                    // if there are as many arguments as values in the collection then
                    // each argument is a key to each value in the order provided.
                    else if (IsCollection(value))
                    {
                        IList list = value as IList;
                        if (arguments.Length == (list).Count)
                        {
                            int index = 0;
                            foreach (object key in arguments)
                            {
                                dictionary.Add(key, (list)[index]);
                                index++;
                            }
                        }
                    }
                }
                // Guids require special treatment because convert will not handle conversion
                // between strings and Guids.
                else if (propertyType.Equals(typeof(Guid)))
                {
                    propertyInfo.SetValue(target, new Guid(value.ToString()), arguments);
                }
            }
            catch (System.IndexOutOfRangeException ex)
            {
                Throw(string.Concat("Error reading property, ", propertyName, ", from type ",
                                    target.GetType().Name), ex);
            }
            catch (System.InvalidCastException ex)
            {
                Throw(string.Concat("Error reading property, ", propertyName, ", from type ",
                                    target.GetType().Name), ex);
            }
            catch (System.MemberAccessException ex)
            {
                Throw(string.Concat("Error reading property, ", propertyName, ", from type ",
                                    target.GetType().Name), ex);
            }
            catch (System.ArgumentException ex)
            {
                Throw(string.Concat("Error reading property, ", propertyName, ", from type ",
                                    target.GetType().Name), ex);
            }
        }
        #endregion

        #endregion

        #region Finding

        #region Class Finding
        /// <summary>
        /// Returns a boolean indicating if the field of the provided name exists within this class type
        /// </summary>
        /// <param name="type">The class type to check</param>
        /// <param name="name">Case-sensitive field name</param>
        /// <returns>True if the class type contains a field by the name provided, false if not</returns>
        public static bool HasField(Type type, string name, bool searchHierarchy)
        {
            return (GetMemberInfo(type, name, MemberTypes.Field, searchHierarchy).Count > 0);
        }

        /// <summary>
        /// Returns a boolean indicating if the property of the provided name exists within this class type
        /// </summary>
        /// <param name="type">The class type to check</param>
        /// <param name="name">Case-sensitive property name</param>
        /// <returns>True if the class type contains a property by the name provided, false if not</returns>
        public static bool HasProperty(Type type, string name, bool searchHierarchy)
        {
            return (GetMemberInfo(type, name, MemberTypes.Property, searchHierarchy).Count > 0);
        }

        /// <summary>
        /// Returns a boolean indicating if the method of the provided name exists within this class type
        /// </summary>
        /// <param name="type">The class type to check</param>
        /// <param name="name">Case-sensitive method name</param>
        /// <returns>True if the class type contains a method by the name provided, false if not</returns>
        public static bool HasMethod(Type type, string name, bool searchHierarchy)
        {
            return (GetMemberInfo(type, name, MemberTypes.Method, searchHierarchy).Count > 0);
        }

        /// <summary>
        /// Check if a method has an attribute or not
        /// </summary>
        /// <param name="method">Method to check</param>
        /// <param name="attributeType">Type of attribute</param>
        /// <returns>True if a method has that attribute</returns>
        public static bool HasAttribute(MethodBase method, Type attributeType, bool inherit)
        {
            return method.GetCustomAttributes(attributeType, inherit).Length > 0;
        }

        /// <summary>
        /// Check if a property has an attribute or not
        /// </summary>
        /// <param name="property">Property info</param>
        /// <param name="attributeType">Type of attribute</param>
        /// <returns>True is a property has that attribute</returns>
        public static bool HasAttribute(PropertyInfo property, Type attributeType, bool inherit)
        {
            return property.GetCustomAttributes(attributeType, inherit).Length > 0;
        }

        /// <summary>
        /// Check if a field has an attribute or not
        /// </summary>
        /// <param name="field">Field info</param>
        /// <param name="attributeType">Type of attribute</param>
        /// <returns>True is a field has that attribute</returns>
        public static bool HasAttribute(FieldInfo field, Type attributeType, bool inherit)
        {
            return field.GetCustomAttributes(attributeType, inherit).Length > 0;
        }

        /// <summary>
        /// Check if a member has an attribute
        /// </summary>
        /// <param name="member">Member information</param>
        /// <param name="attributeType">Type of attribute</param>
        /// <returns>True is a member has that attribute</returns>
        public static bool HasAttribute(MemberInfo member, Type attributeType, bool inherit)
        {
            return member.GetCustomAttributes(attributeType, inherit).Length > 0;
        }

        /// <summary>
        /// Check if the method has the correct parameter signature
        /// </summary>
        /// <param name="method">Method to check</param>
        /// <param name="parameterTypes">Parameter signature</param>
        /// <returns>True if the method has the parameter signature</returns>
        public static bool IsParameterTypesMatch(MethodBase method, Type[] parameterTypes)
        {
            ParameterInfo[] parameters = method.GetParameters();
            if (parameterTypes.Length != parameters.Length) return false;

            for (int i = 0; i < parameters.Length; i++)
            {
                if (parameters[i].ParameterType != parameterTypes[i]) return false;
            }

            return true;
        }

        /// <summary>
        /// Is the supplied PropertyName the default indexer for the supplied Type
        /// </summary>
        /// <param name="propertyName">The name of the property on the supplied Type to be checked</param>
        /// <param name="type">The Type to be checked</param>
        /// <returns>True if the supplied PropertyName is the default indexer for the supplied Type</returns>
        public static bool PropertyIsIndexer(Type type, string propertyName)
        {
            DefaultMemberAttribute[] attribs = (DefaultMemberAttribute[])type.GetCustomAttributes(typeof(DefaultMemberAttribute), true);
            if (attribs.Length != 0)
            {
                foreach (DefaultMemberAttribute attrib in attribs)
                {
                    if (attrib.MemberName.Equals(propertyName))
                    {
                        return true;
                    }
                }
            }
            return false;
        }
        #endregion

        #region Attribute Finding
        /// <summary>
        /// Get constructor by its attribute
        /// </summary>
        /// <param name="type">Type of class</param>
        /// <param name="attributeType">Type of attribute</param>
        /// <returns>Information of the constructord which have that attribute</returns>
        public static ConstructorInfo[] GetContructorsByAttribute(Type type, Type attributeType)
        {
            List<ConstructorInfo> results = new List<ConstructorInfo>();
            foreach (ConstructorInfo constructor in type.GetConstructors())
            {
                if (constructor.GetCustomAttributes(attributeType, true).Length > 0)
                {
                    results.Add(constructor);
                }
            }
            return results.ToArray();
        }

        /// <summary>
        /// List all methods by attribute
        /// </summary>
        /// <param name="type">Type of class</param>
        /// <param name="attributeType">Type of attribute</param>
        /// <returns>List of methods associated with the attribute</returns>
        public static MethodInfo[] GetMethodsByAttribute(Type type, Type attributeType)
        {
            List<MethodInfo> results = new List<MethodInfo>();
            foreach (MethodInfo method in type.GetMethods())
            {
                if (method.GetCustomAttributes(attributeType, true).Length > 0)
                {
                    results.Add(method);
                }
            }
            return results.ToArray();
        }

        /// <summary>
        /// Get a list of properties by attribute
        /// </summary>
        /// <param name="type">Type of class</param>
        /// <param name="attributeType">Type of attribute</param>
        /// <returns>List of property information</returns>
        public static PropertyInfo[] GetPropertiesByAttribute(Type type, Type attributeType)
        {
            List<PropertyInfo> results = new List<PropertyInfo>();
            foreach (PropertyInfo property in type.GetProperties())
            {
                if (property.GetCustomAttributes(attributeType, true).Length > 0)
                {
                    results.Add(property);
                }
            }
            return results.ToArray();
        }

        /// <summary>
        /// Get a list of fields by attribute
        /// </summary>
        /// <param name="type">Type of class</param>
        /// <param name="attributeType">type of attribute</param>
        /// <returns>List of field information</returns>
        public static FieldInfo[] GetFieldsByAttribute(Type type, Type attributeType)
        {
            List<FieldInfo> results = new List<FieldInfo>();
            foreach (FieldInfo field in type.GetFields())
            {
                if (field.GetCustomAttributes(attributeType, true).Length > 0)
                {
                    results.Add(field);
                }
            }
            return results.ToArray();
        }

        /// <summary>
        /// Get a list of members by attribute
        /// </summary>
        /// <param name="type">Type of class</param>
        /// <param name="attributeType">Type of attribute</param>
        /// <returns>List of member infomation</returns>
        public static MemberInfo[] GetMembersByAttribute(Type type, Type attributeType)
        {
            List<MemberInfo> results = new List<MemberInfo>();
            foreach (MemberInfo member in type.GetMembers())
            {
                if (member.GetCustomAttributes(attributeType, true).Length > 0)
                {
                    results.Add(member);
                }
            }
            return results.ToArray();
        }
        #endregion

        #endregion

        #region Common
        /// <summary>
        /// Determine if the provided instance implements the ICollection interface
        /// </summary>
        /// <param name="instance">The class instance to check</param>
        /// <returns>True if the class instance implements ICollection, false if it does not</returns>
        public static bool IsCollection(object instance)
        {
            return ImplementsInterface(instance, "ICollection");
        }

        /// <summary>
        /// Determine if the provided type implements the ICollection interface
        /// </summary>
        /// <param name="type">The type to check</param>
        /// <returns>True if the type implements ICollection, false if it does not</returns>
        public static bool IsCollection(Type type)
        {
            return ImplementsInterface(type, "ICollection");
        }

        /// <summary>
        /// Determine if the provided instance implements the IDictionary interface
        /// </summary>
        /// <param name="instance">The class instance to check</param>
        /// <returns>True if the class instance implements IDictionary, false if it does not</returns>
        public static bool IsDictionary(object instance)
        {
            return ImplementsInterface(instance, "IDictionary");
        }

        /// <summary>
        /// Determine if the provided type implements the IDictionary interface
        /// </summary>
        /// <param name="instance">The type to check</param>
        /// <returns>True if the type implements IDictionary, false if it does not</returns>
        public static bool IsDictionary(Type type)
        {
            return ImplementsInterface(type, "IDictionary");
        }

        /// <summary>
        /// Determine if the provided instance implements the IEnumerable interface
        /// </summary>
        /// <param name="instance">The class instance to check</param>
        /// <returns>True if the class instance implements IEnumerable, false if it does not</returns>
        public static bool IsEnumerable(object instance)
        {
            return ImplementsInterface(instance, "IEnumerable");
        }

        /// <summary>
        /// Determine if the provided type implements the IEnumerable interface
        /// </summary>
        /// <param name="instance">The type to check</param>
        /// <returns>True if the type implements IEnumerable, false if it does not</returns>
        public static bool IsEnumerable(Type type)
        {
            return ImplementsInterface(type, "IEnumerable");
        }

        /// <summary>
        /// Determine if the provided instance implements the interface or not
        /// </summary>
        /// <param name="instance">The class instance to check</param>
        /// <param name="interfaceName">The interface name to check</param>
        /// <returns>True if the class instance implements the interface, false if it does not</returns>
        public static bool ImplementsInterface(object instance, string interfaceName)
        {
            return ImplementsInterface(instance.GetType(), interfaceName);
        }

        /// <summary>
        /// Determine if the provided type implements the interface or not
        /// </summary>
        /// <param name="instance">The type to check</param>
        /// <param name="interfaceName">The interface name to check</param>
        /// <returns>True if the type implements the interface, false if it does not</returns>
        public static bool ImplementsInterface(Type type, string interfaceName)
        {
            return type.GetInterface(interfaceName) != null;
        }
        #endregion

        #region Private
        /// <summary>
        /// Get all fields of a type
        /// </summary>
        /// <param name="type">Type to get</param>
        /// <returns>List of FieldInfo</returns>
        private static FieldInfo[] GetFields(Type type)
        {
            ArrayList fieldList = new ArrayList();
            CollectFieldsRecursive(type, fieldList);
            FieldInfo[] fields = (FieldInfo[])fieldList.ToArray(typeof(FieldInfo));
            return fields;
        }

        /// <summary>
        /// Recursively collect all fields of a type
        /// </summary>
        /// <param name="type">Type to collect</param>
        /// <param name="fieldList">List of collected fields</param>
        private static void CollectFieldsRecursive(Type type, ArrayList fieldList)
        {
            if (type == typeof(object)) return;

            FieldInfo[] fields = type.GetFields(BindingFlags.Instance | BindingFlags.DeclaredOnly | BindingFlags.Public | BindingFlags.NonPublic);
            fieldList.AddRange(fields);
            CollectFieldsRecursive(type.BaseType, fieldList);
        }

        /// <summary>
        /// Returns a specific MethodInfo object from the type
        /// </summary>
        /// <param name="type">The System.Type to search</param>
        /// <param name="methodName">The case sensitive method name</param>
        /// <param name="searchHierarchy">A boolean indicating whether or not to search inherited methods</param>
        /// <returns>A list of MethodInfo objects matching the constraints provided</returns>
        private static List<MethodInfo> GetMethodInfo(Type type, string methodName, bool searchHierarchy)
        {
            MethodInfo methodInfo = type.GetMethod(methodName, instanceFlag);

            if (searchHierarchy && methodInfo == null & type.BaseType != typeof(object))
            {
                return GetMethodInfo(type, methodName, true);
            }
            return new List<MethodInfo>(new MethodInfo[] { methodInfo });
        }

        /// <summary>
        /// Return an ordered array of Types for each of the instances in instanceArray.
        /// </summary>
        /// <param name="instanceArray">An array of object instances</param>
        /// <returns>A Type array with the ordered Types of the objects</returns>
        private static Type[] GetTypeArray(object[] instanceArray)
        {
            Type[] types = new Type[instanceArray.Length];

            for (int index = 0; index < instanceArray.Length; index++)
            {
                types[index] = instanceArray[index].GetType();
            }

            return types;
        }

        /// <summary>
        /// rethrows the error message specified as a reflectorexception.
        /// </summary>
        /// <param name="ex">the error to re-throw</param>
        private static void Throw(Exception ex)
        {
            throw ex;
        }

        /// <summary>
        /// Rethrows the error message specified as a ReflectorException.
        /// </summary>
        /// <param name="errorMessage">The error message to create</param>
        private static void Throw(string errorMessage)
        {
            throw new ReflectionException(errorMessage);
        }

        /// <summary>
        /// Rethrows the error message specified as a ReflectorException.
        /// </summary>
        /// <param name="errorMessage">The error message to create</param>
        /// <param name="innerException">The exception which occurred to provide as an innerException</param>
        private static void Throw(string errorMessage, Exception innerException)
        {
            throw new ReflectionException(errorMessage, innerException);
        }
        #endregion

        #region AOP Utilities
        public static object CreateObject(Type fromType)
        {
            return Activator.CreateInstance(fromType);
        }

        public static object CreateObject(string assemblyName, string typeName)
        {
            ObjectHandle obj = Activator.CreateInstance(assemblyName, typeName);
            return obj.Unwrap();
        }

        public static object CreateObject(string typePath)
        {
            int index = typePath.IndexOf(",");
            string typeName = typePath.Substring(0, index);
            string assemblyName = typePath.Substring(index + 1);
            return CreateObject(assemblyName, typeName);
        }

        /// <summary>
        /// Get all MethodInfo from a pattern
        /// </summary>
        /// <param name="typePattern"></param>
        /// <returns></returns>
        public static MethodInfo[] GetMethodsFromPattern(string typePattern)
        {
            ArrayList array = new ArrayList();
            if (string.IsNullOrEmpty(typePattern))
            {
                throw new ConfigurationErrorsException("Invalid configuration.");
            }
            else
            {
                int index = typePattern.IndexOf(",");
                if (index <= 0 || index >= typePattern.Length - 2)
                    throw new ConfigurationErrorsException("Invalid configuration.");

                string assemblyName = typePattern.Substring(index + 1);
                typePattern = typePattern.Substring(0, index).Trim();
                Assembly asm = Assembly.Load(assemblyName);
                if (asm != null) //load successfully
                {
                    BindingFlags bindingFlags = BindingFlags.Public | BindingFlags.InvokeMethod | BindingFlags.Instance | BindingFlags.DeclaredOnly;
                    if (typePattern.EndsWith(".*")) //Get method info of all public methods in an assembly or a class
                    {
                        typePattern = typePattern.Remove(typePattern.Length - 2);
                        foreach (Type type in asm.GetTypes())
                        {
                            if (type.FullName.StartsWith(typePattern) && type.IsClass)
                                array.AddRange(type.GetMethods(bindingFlags));
                        }
                    }
                    else //Get method info of a specific method of an class
                    {
                        foreach (Type type in asm.GetTypes())
                        {
                            if (typePattern.StartsWith(type.FullName) && type.IsClass)
                                foreach (MethodInfo method in type.GetMethods(bindingFlags))
                                    if (typePattern.EndsWith(method.Name) && typePattern.Length - type.FullName.Length == method.Name.Length + 1)
                                        array.Add(method);
                        }
                    }
                }
            }
            return (MethodInfo[])array.ToArray(typeof(MethodInfo));
        }
        #endregion
    }
}
